#include <gtest/gtest.h>
#include <memory>

#include "common_components/common_runtime/src/heap/collector/collector_proxy.h"
#include "common_components/common_runtime/src/heap/w_collector/w_collector.h"
#include "common_components/common_runtime/src/common/page_allocator.h"
#include "common_components/common_runtime/src/heap/allocator/allocator.h"


using namespace panda;

// ==================== Stub Classes ====================

class TestWCollector : public WCollector {
public:
    explicit TestWCollector(Allocator& allocator, CollectorResources& resources)
        : WCollector(allocator, resources) {}

    void RunGarbageCollection(uint64_t gcIndex, GCReason reason) override {
        called_ = true;
        gcIndex_ = gcIndex;
        reason_ = reason;
    }

    bool IsGcStarted() const  { return started_; }
    void SetGcStarted(bool started)  { started_ = started; }

    bool called_ = false;
    uint64_t gcIndex_ = 0;
    GCReason reason_ = GC_REASON_INVALID;
    bool started_ = false;
};

class TestPageAllocator : public PageAllocator, public Allocator {
public:
    explicit TestPageAllocator(uint16_t size) : PageAllocator(size) {}

    HeapAddress Allocate(size_t size, AllocType allocType) override { return 0; }
    HeapAddress AllocateNoGC(size_t size, AllocType allocType) override { return 0; }
    bool ForEachObject(const std::function<void(BaseObject*)>& callback, bool safe) const override {
        return true;
    }
    size_t ReclaimGarbageMemory(bool releaseAll) override { return 0; }
    void FeedHungryBuffers() override {}
    size_t LargeObjectSize() const override { return 0; }
    size_t GetAllocatedBytes() const override { return 0; }
    void Init(const RuntimeParam& param) override {}
    size_t GetMaxCapacity() const override { return 1024 * 1024 * 10; }
    size_t GetCurrentCapacity() const override { return 1024 * 1024 * 5; }
    size_t GetUsedPageSize() const override { return 0; }
    HeapAddress GetSpaceStartAddress() const override { return 0; }
    HeapAddress GetSpaceEndAddress() const override { return 0; }
    bool IsHeapObject(HeapAddress addr) const override { return false; }
};

class TestCollectorResources : public CollectorResources {
public:
    explicit TestCollectorResources(CollectorProxy& proxy) : CollectorResources(proxy) {}

    void Init()  {}   // 基类是 virtual 函数
    void Fini()  {}   // 同上
};

// ==================== Testable Proxy ====================

class TestableCollectorProxy : public CollectorProxy {
public:
    using CollectorProxy::CollectorProxy;

    friend class CollectorProxyTest;

    TraceCollector*& GetCurrentCollectorPtr() { return currentCollector_; }
    WCollector& GetWCollector() { return wCollector_; }
};

using TestCollectorProxy = TestableCollectorProxy;

// ==================== Test Fixture ====================

class CollectorProxyTest : public ::testing::Test {
protected:
    void SetUp() override {
        // Step 1: 构造 dummy proxy 和 resources
        proxyHolder_.Init({});
        resourcesHolder_.Init();

        // Step 2: 构造资源对象
        resources_.reset(new TestCollectorResources(proxyHolder_));

        // Step 3: 构造 proxy
        proxy_.reset(new TestableCollectorProxy(allocator_, *resources_));

        // Step 4: 初始化 proxy
        proxy_->Init({});
    }

    void TearDown() override {
        if (proxy_) {
            proxy_->Fini();
        }
    }

    TestPageAllocator allocator_{512};
    CollectorProxy proxyHolder_{allocator_, resourcesHolder_};  // dummy proxy for dependency
    TestCollectorResources resourcesHolder_{proxyHolder_};      // dummy resources
    std::unique_ptr<TestCollectorResources> resources_;
    std::unique_ptr<TestableCollectorProxy> proxy_;
};

// ==================== Parameterized Test Class ====================

class CollectorProxyRunGCParamTest
    : public CollectorProxyTest,
      public ::testing::WithParamInterface<GCReason> {};

// ==================== Test Case: RunGarbageCollection ====================

TEST_F(CollectorProxyTest, RunGarbageCollection_ForUnknownReason_CallsWCollector) {
    struct MockWCollector final : public WCollector {
        using WCollector::WCollector;

        void RunGarbageCollection(uint64_t gcIndex, GCReason reason) override {
            called = true;
            index = gcIndex;
            reason_ = reason;
        }

        bool IsGcStarted() const  { return started; }
        void SetGcStarted(bool s)  { started = s; }

        bool called = false;
        uint64_t index = 0;
        GCReason reason_ = GC_REASON_INVALID;
        bool started = false;
    };

    // 正确构造方式：解引用 resources_
    MockWCollector testWCollector(allocator_, *resources_);
    proxy_->GetCurrentCollectorPtr() = &testWCollector;

    proxy_->RunGarbageCollection(101, static_cast<GCReason>(99));

    EXPECT_TRUE(testWCollector.called);
    EXPECT_EQ(testWCollector.index, static_cast<uint64_t>(101));
    EXPECT_EQ(testWCollector.reason_, static_cast<GCReason>(99));
}

// ==================== Parameterized Test Case ====================

TEST_P(CollectorProxyRunGCParamTest, RunGarbageCollection_ForDifferentReasons_CallsWCollector) {
    struct MockWCollector final : public WCollector {
        using WCollector::WCollector;

        void RunGarbageCollection(uint64_t gcIndex, GCReason reason) override {
            runCalled = true;
            lastGcIndex = gcIndex;
            lastReason = reason;
        }

        bool IsGcStarted() const  { return gcStarted; }
        void SetGcStarted(bool started)  { gcStarted = started; }

        bool runCalled = false;
        uint64_t lastGcIndex = 0;
        GCReason lastReason = GC_REASON_INVALID;
        bool gcStarted = false;
    };

    // 构造 mock collector 并设置到 proxy 中
    MockWCollector testWCollector(allocator_, *resources_);
    proxy_->GetCurrentCollectorPtr() = &testWCollector;

    const uint64_t testGcIndex = 200;
    // const GCReason testReason = GetParam();
    const GCReason testReason = GC_REASON_HEU;

    // 执行被测方法
    proxy_->RunGarbageCollection(testGcIndex, testReason);

    // 验证逻辑
    EXPECT_TRUE(testWCollector.runCalled);
    EXPECT_EQ(testWCollector.lastGcIndex, testGcIndex);
    EXPECT_EQ(testWCollector.lastReason, testReason);
    EXPECT_TRUE(testWCollector.gcStarted);
}

// ==================== Test Instantiation ====================

INSTANTIATE_TEST_SUITE_P(
    AllGCReasons,
    CollectorProxyRunGCParamTest,
    ::testing::Values(
        GC_REASON_HEU,
        GC_REASON_BACKUP,
        GC_REASON_OOM,
        GC_REASON_FORCE,
        static_cast<GCReason>(99)));